//
// Created by haoy on 2017/4/29.
//

#ifndef RTC_XSWITCH2_STAT_HPP
#define RTC_XSWITCH2_STAT_HPP

#include <mutex>
#include <string>
#include <vector>

template <class Value>
class Optional {
public:
    Optional() : is_valid_(false) {}
    operator bool() const {
        return is_valid_;
    }

    Value Get() const {
        return value_;
    }

    void Set(const Value& v) {
        value_ = v;
        is_valid_ = true;
    }

    void Clear() {
        is_valid_ = false;
    }

private:
    bool is_valid_;
    Value value_;
};

template <class ValueType, size_t ReservoirSize = 8>
class Histogram {
public:
    Histogram() {}

    void Update(ValueType v) {
        reservoirSampling(v);
    }

    void Update(const Histogram<ValueType, ReservoirSize>& h) {
        for (auto s : h.samples_) {
            reservoirSampling(s);
        }
    }

    // Reservoir Sampling is taken from Algorithm R in https://www.cs.umd.edu/~samir/498/vitter.pdf
    // see also https://en.wikipedia.org/wiki/Reservoir_sampling
    void reservoirSampling(ValueType v) {
        if (samples_.size() < ReservoirSize) {
            samples_.push_back(v);
        }
        t_ += 1;
        double r = std::rand();
        r /= RAND_MAX;
        int m = r * t_;
        if (m < ReservoirSize) {
            samples_[m] = v;
        }
    }

    void Clear() {
        samples_.clear();
        t_ = 0;
    }

    Optional<ValueType> Min() {
        if (samples_.empty()) {
            return Optional<ValueType>();
        }
        Optional<ValueType> o;
        for (auto v : samples_) {
            if (!o || v < o.Get()) {
                o.Set(v);
            }
        }
        return o;
    }

    Optional<ValueType> Max() {
        if (samples_.empty()) {
            return Optional<ValueType>();
        }
        Optional<ValueType> o;
        for (auto v : samples_) {
            if (!o || v > o.Get()) {
                o.Set(v);
            }
        }
        return o;
    }

    Optional<ValueType> Avg() {
        if (samples_.empty()) {
            return Optional<ValueType>();
        }
        Optional<ValueType> r;
        ValueType sum = 0;
        for (auto v : samples_) {
            sum += v;
        }
        r.Set(sum / samples_.size());
        return r;
    }

    std::vector<ValueType>& Samples() {
        return samples_;
    }

private:
    std::vector<ValueType>  samples_;
    size_t                  t_ = 0;
};

template <class ValueType>
class Counter {
public:
    Counter() {};

    Optional<ValueType> Get() const {
        return v_;
    }

    void Update(const Counter<ValueType>& c) {
        if (!c.v_) {
            return;
        } else if (!v_) {
            v_.Set(c.v_.Get());
        } else {
            v_.Set(v_.Get() + c.v_.Get());
        }
    }

    void Update(ValueType v) {
        if (!v_) {
            v_.Set(v);
        } else {
            v_.Set(v_.Get() + v);
        }
    }

    void Clear() {
        v_.Clear();
    }

private:
    Optional<ValueType> v_;
};

#endif //RTC_XSWITCH2_STAT_HPP
